home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / nt / source.exe / POSIX / MAKE / VAR.C < prev   
C/C++ Source or Header  |  1992-09-21  |  55KB  |  1,890 lines

  1. /*
  2.  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
  3.  * Copyright (c) 1988, 1989 by Adam de Boor
  4.  * Copyright (c) 1989 by Berkeley Softworks
  5.  * All rights reserved.
  6.  *
  7.  * This code is derived from software contributed to Berkeley by
  8.  * Adam de Boor.
  9.  *
  10.  * Redistribution and use in source and binary forms, with or without
  11.  * modification, are permitted provided that the following conditions
  12.  * are met:
  13.  * 1. Redistributions of source code must retain the above copyright
  14.  *    notice, this list of conditions and the following disclaimer.
  15.  * 2. Redistributions in binary form must reproduce the above copyright
  16.  *    notice, this list of conditions and the following disclaimer in the
  17.  *    documentation and/or other materials provided with the distribution.
  18.  * 3. All advertising materials mentioning features or use of this software
  19.  *    must display the following acknowledgement:
  20.  *    This product includes software developed by the University of
  21.  *    California, Berkeley and its contributors.
  22.  * 4. Neither the name of the University nor the names of its contributors
  23.  *    may be used to endorse or promote products derived from this software
  24.  *    without specific prior written permission.
  25.  *
  26.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  27.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  30.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  31.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  32.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  33.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  34.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  35.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  36.  * SUCH DAMAGE.
  37.  */
  38.  
  39. #ifndef lint
  40. static char sccsid[] = "@(#)var.c    5.7 (Berkeley) 6/1/90";
  41. #endif /* not lint */
  42.  
  43. /*-
  44.  * var.c --
  45.  *    Variable-handling functions
  46.  *
  47.  * Interface:
  48.  *    Var_Set              Set the value of a variable in the given
  49.  *                      context. The variable is created if it doesn't
  50.  *                      yet exist. The value and variable name need not
  51.  *                      be preserved.
  52.  *
  53.  *    Var_Append        Append more characters to an existing variable
  54.  *                      in the given context. The variable needn't
  55.  *                      exist already -- it will be created if it doesn't.
  56.  *                      A space is placed between the old value and the
  57.  *                      new one.
  58.  *
  59.  *    Var_Exists        See if a variable exists.
  60.  *
  61.  *    Var_Value         Return the value of a variable in a context or
  62.  *                      NULL if the variable is undefined.
  63.  *
  64.  *    Var_Subst         Substitute for all variables in a string using
  65.  *                      the given context as the top-most one. If the
  66.  *                      third argument is non-zero, Parse_Error is
  67.  *                      called if any variables are undefined.
  68.  *
  69.  *    Var_Parse         Parse a variable expansion from a string and
  70.  *                      return the result and the number of characters
  71.  *                      consumed.
  72.  *
  73.  *    Var_Delete        Delete a variable in a context.
  74.  *
  75.  *    Var_Init          Initialize this module.
  76.  *
  77.  * Debugging:
  78.  *    Var_Dump          Print out all variables defined in the given
  79.  *                      context.
  80.  *
  81.  * XXX: There's a lot of duplication in these functions.
  82.  */
  83.  
  84. #include    <ctype.h>
  85. #include    "make.h"
  86. #include    "buf.h"
  87. extern char *getenv();
  88.  
  89. /*
  90.  * This is a harmless return value for Var_Parse that can be used by Var_Subst
  91.  * to determine if there was an error in parsing -- easier than returning
  92.  * a flag, as things outside this module don't give a hoot.
  93.  */
  94. char     var_Error[] = "";
  95.  
  96. /*
  97.  * Similar to var_Error, but returned when the 'err' flag for Var_Parse is
  98.  * set false. Why not just use a constant? Well, gcc likes to condense
  99.  * identical string instances...
  100.  */
  101. char    varNoError[] = "";
  102.  
  103. /*
  104.  * Internally, variables are contained in four different contexts.
  105.  *    1) the environment. They may not be changed. If an environment
  106.  *        variable is appended-to, the result is placed in the global
  107.  *        context.
  108.  *    2) the global context. Variables set in the Makefile are located in
  109.  *        the global context. It is the penultimate context searched when
  110.  *        substituting.
  111.  *    3) the command-line context. All variables set on the command line
  112.  *       are placed in this context. They are UNALTERABLE once placed here.
  113.  *    4) the local context. Each target has associated with it a context
  114.  *       list. On this list are located the structures describing such
  115.  *       local variables as $(@) and $(*)
  116.  * The four contexts are searched in the reverse order from which they are
  117.  * listed.
  118.  */
  119. GNode          *VAR_GLOBAL;   /* variables from the makefile */
  120. GNode          *VAR_CMD;      /* variables defined on the command-line */
  121.  
  122. #define FIND_CMD    0x1   /* look in VAR_CMD when searching */
  123. #define FIND_GLOBAL    0x2   /* look in VAR_GLOBAL as well */
  124. #define FIND_ENV      0x4   /* look in the environment also */
  125.  
  126. typedef struct Var {
  127.     char          *name;    /* the variable's name */
  128.     Buffer      val;            /* its value */
  129.     int              flags;        /* miscellaneous status flags */
  130. #define VAR_IN_USE    1           /* Variable's value currently being used.
  131.                      * Used to avoid recursion */
  132. #define VAR_FROM_ENV    2           /* Variable comes from the environment */
  133. #define VAR_JUNK      4           /* Variable is a junk variable that
  134.                      * should be destroyed when done with
  135.                      * it. Used by Var_Parse for undefined,
  136.                      * modified variables */
  137. }  Var;
  138.  
  139. /*-
  140.  *-----------------------------------------------------------------------
  141.  * VarCmp  --
  142.  *    See if the given variable matches the named one. Called from
  143.  *    Lst_Find when searching for a variable of a given name.
  144.  *
  145.  * Results:
  146.  *    0 if they match. non-zero otherwise.
  147.  *
  148.  * Side Effects:
  149.  *    none
  150.  *-----------------------------------------------------------------------
  151.  */
  152. static int
  153. VarCmp (v, name)
  154.     Var            *v;        /* VAR structure to compare */
  155.     char           *name;    /* name to look for */
  156. {
  157.     return (strcmp (name, v->name));
  158. }
  159.  
  160. /*-
  161.  *-----------------------------------------------------------------------
  162.  * VarFind --
  163.  *    Find the given variable in the given context and any other contexts
  164.  *    indicated.
  165.  *
  166.  * Results:
  167.  *    A pointer to the structure describing the desired variable or
  168.  *    NIL if the variable does not exist.
  169.  *
  170.  * Side Effects:
  171.  *    None
  172.  *-----------------------------------------------------------------------
  173.  */
  174. static Var *
  175. VarFind (name, ctxt, flags)
  176.     char               *name;    /* name to find */
  177.     GNode              *ctxt;    /* context in which to find it */
  178.     int                 flags;    /* FIND_GLOBAL set means to look in the
  179.                  * VAR_GLOBAL context as well.
  180.                  * FIND_CMD set means to look in the VAR_CMD
  181.                  * context also.
  182.                  * FIND_ENV set means to look in the
  183.                  * environment */
  184. {
  185.     LstNode             var;
  186.     Var              *v;
  187.  
  188.     /*
  189.      * If the variable name begins with a '.', it could very well be one of
  190.      * the local ones.  We check the name against all the local variables
  191.      * and substitute the short version in for 'name' if it matches one of
  192.      * them.
  193.      */
  194.     if (*name == '.' && isupper(name[1]))
  195.         switch (name[1]) {
  196.         case 'A':
  197.             if (!strcmp(name, ".ALLSRC"))
  198.                 name = ALLSRC;
  199.             if (!strcmp(name, ".ARCHIVE"))
  200.                 name = ARCHIVE;
  201.             break;
  202.         case 'I':
  203.             if (!strcmp(name, ".IMPSRC"))
  204.                 name = IMPSRC;
  205.             break;
  206.         case 'M':
  207.             if (!strcmp(name, ".MEMBER"))
  208.                 name = MEMBER;
  209.             break;
  210.         case 'O':
  211.             if (!strcmp(name, ".OODATE"))
  212.                 name = OODATE;
  213.             break;
  214.         case 'P':
  215.             if (!strcmp(name, ".PREFIX"))
  216.                 name = PREFIX;
  217.             break;
  218.         case 'T':
  219.             if (!strcmp(name, ".TARGET"))
  220.                 name = TARGET;
  221.             break;
  222.         }
  223.     /*
  224.      * First look for the variable in the given context. If it's not there,
  225.      * look for it in VAR_CMD, VAR_GLOBAL and the environment, in that order,
  226.      * depending on the FIND_* flags in 'flags'
  227.      */
  228.     var = Lst_Find (ctxt->context, (ClientData)name, VarCmp);
  229.  
  230.     if ((var == NILLNODE) && (flags & FIND_CMD) && (ctxt != VAR_CMD)) {
  231.     var = Lst_Find (VAR_CMD->context, (ClientData)name, VarCmp);
  232.     }
  233.     if (!checkEnvFirst && (var == NILLNODE) && (flags & FIND_GLOBAL) &&
  234.     (ctxt != VAR_GLOBAL))
  235.     {
  236.     var = Lst_Find (VAR_GLOBAL->context, (ClientData)name, VarCmp);
  237.     }
  238.     if ((var == NILLNODE) && (flags & FIND_ENV)) {
  239.     char *env;
  240.  
  241.     if ((env = getenv (name)) != NULL) {
  242.         /*
  243.          * If the variable is found in the environment, we only duplicate
  244.          * its value (since eVarVal was allocated on the stack). The name
  245.          * doesn't need duplication since it's always in the environment
  246.          */
  247.         int          len;
  248.         
  249.         v = (Var *) emalloc(sizeof(Var));
  250.         v->name = name;
  251.  
  252.         len = strlen(env);
  253.         
  254.         v->val = Buf_Init(len);
  255.         Buf_AddBytes(v->val, len, (Byte *)env);
  256.         
  257.         v->flags = VAR_FROM_ENV;
  258.         return (v);
  259.     } else if (checkEnvFirst && (flags & FIND_GLOBAL) &&
  260.            (ctxt != VAR_GLOBAL))
  261.     {
  262.         var = Lst_Find (VAR_GLOBAL->context, (ClientData)name, VarCmp);
  263.         if (var == NILLNODE) {
  264.         return ((Var *) NIL);
  265.         } else {
  266.         return ((Var *)Lst_Datum(var));
  267.         }
  268.     } else {
  269.         return((Var *)NIL);
  270.     }
  271.     } else if (var == NILLNODE) {
  272.     return ((Var *) NIL);
  273.     } else {
  274.     return ((Var *) Lst_Datum (var));
  275.     }
  276. }
  277.  
  278. /*-
  279.  *-----------------------------------------------------------------------
  280.  * VarAdd  --
  281.  *    Add a new variable of name name and value val to the given context
  282.  *
  283.  * Results:
  284.  *    None
  285.  *
  286.  * Side Effects:
  287.  *    The new variable is placed at the front of the given context
  288.  *    The name and val arguments are duplicated so they may
  289.  *    safely be freed.
  290.  *-----------------------------------------------------------------------
  291.  */
  292. static
  293. VarAdd (name, val, ctxt)
  294.     char           *name;    /* name of variable to add */
  295.     char           *val;    /* value to set it to */
  296.     GNode          *ctxt;    /* context in which to set it */
  297. {
  298.     register Var   *v;
  299.     int              len;
  300.  
  301.     v = (Var *) emalloc (sizeof (Var));
  302.  
  303.     v->name = strdup (name);
  304.  
  305.     len = strlen(val);
  306.     v->val = Buf_Init(len+1);
  307.     Buf_AddBytes(v->val, len, (Byte *)val);
  308.  
  309.     v->flags = 0;
  310.  
  311.     (void) Lst_AtFront (ctxt->context, (ClientData)v);
  312.     if (DEBUG(VAR)) {
  313.     printf("%s:%s = %s\n", ctxt->name, name, val);
  314.     }
  315. }
  316.  
  317. /*-
  318.  *-----------------------------------------------------------------------
  319.  * Var_Delete --
  320.  *    Remove a variable from a context.
  321.  *
  322.  * Results:
  323.  *    None.
  324.  *
  325.  * Side Effects:
  326.  *    The Var structure is removed and freed.
  327.  *
  328.  *-----------------------------------------------------------------------
  329.  */
  330. void
  331. Var_Delete(name, ctxt)
  332.     char          *name;
  333.     GNode      *ctxt;
  334. {
  335.     LstNode       ln;
  336.  
  337.     if (DEBUG(VAR)) {
  338.     printf("%s:delete %s\n", ctxt->name, name);
  339.     }
  340.     ln = Lst_Find(ctxt->context, (ClientData)name, VarCmp);
  341.     if (ln != NILLNODE) {
  342.     register Var       *v;
  343.  
  344.     v = (Var *)Lst_Datum(ln);
  345.     Lst_Remove(ctxt->context, ln);
  346.     Buf_Destroy(v->val, TRUE);
  347.     free(v->name);
  348.     free((char *)v);
  349.     }
  350. }
  351.  
  352. /*-
  353.  *-----------------------------------------------------------------------
  354.  * Var_Set --
  355.  *    Set the variable name to the value val in the given context.
  356.  *
  357.  * Results:
  358.  *    None.
  359.  *
  360.  * Side Effects:
  361.  *    If the variable doesn't yet exist, a new record is created for it.
  362.  *    Else the old value is freed and the new one stuck in its place
  363.  *
  364.  * Notes:
  365.  *    The variable is searched for only in its context before being
  366.  *    created in that context. I.e. if the context is VAR_GLOBAL,
  367.  *    only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only
  368.  *    VAR_CMD->context is searched. This is done to avoid the literally
  369.  *    thousands of unnecessary strcmp's that used to be done to
  370.  *    set, say, $(@) or $(<).
  371.  *-----------------------------------------------------------------------
  372.  */
  373. void
  374. Var_Set (name, val, ctxt)
  375.     char           *name;    /* name of variable to set */
  376.     char           *val;    /* value to give to the variable */
  377.     GNode          *ctxt;    /* context in which to set it */
  378. {
  379.     register Var   *v;
  380.  
  381.     /*
  382.      * We only look for a variable in the given context since anything set
  383.      * here will override anything in a lower context, so there's not much
  384.      * point in searching them all just to save a bit of memory...
  385.      */
  386.     v = VarFind (name, ctxt, 0);
  387.     if (v == (Var *) NIL) {
  388.     VarAdd (name, val, ctxt);
  389.     } else {
  390.     Buf_Discard(v->val, Buf_Size(v->val));
  391.     Buf_AddBytes(v->val, strlen(val), (Byte *)val);
  392.  
  393.     if (DEBUG(VAR)) {
  394.         printf("%s:%s = %s\n", ctxt->name, name, val);
  395.     }
  396.     }
  397.     /*
  398.      * Any variables given on the command line are automatically exported
  399.      * to the environment (as per POSIX standard)
  400.      */
  401.     if (ctxt == VAR_CMD) {
  402. /*
  403. #ifdef _POSIX_SOURCE
  404.     char
  405.         sbuf [80];
  406.  
  407.     sprintf (sbuf, "%s=%s", name, val);
  408.     putenv (sbuf);
  409. #else
  410. */
  411.     setenv(name, val);
  412. //#endif
  413.     }
  414. }
  415.  
  416. /*-
  417.  *-----------------------------------------------------------------------
  418.  * Var_Append --
  419.  *    The variable of the given name has the given value appended to it in
  420.  *    the given context.
  421.  *
  422.  * Results:
  423.  *    None
  424.  *
  425.  * Side Effects:
  426.  *    If the variable doesn't exist, it is created. Else the strings
  427.  *    are concatenated (with a space in between).
  428.  *
  429.  * Notes:
  430.  *    Only if the variable is being sought in the global context is the
  431.  *    environment searched.
  432.  *    XXX: Knows its calling circumstances in that if called with ctxt
  433.  *    an actual target, it will only search that context since only
  434.  *    a local variable could be being appended to. This is actually
  435.  *    a big win and must be tolerated.
  436.  *-----------------------------------------------------------------------
  437.  */
  438. void
  439. Var_Append (name, val, ctxt)
  440.     char           *name;    /* Name of variable to modify */
  441.     char           *val;    /* String to append to it */
  442.     GNode          *ctxt;    /* Context in which this should occur */
  443. {
  444.     register Var   *v;
  445.     register char  *cp;
  446.  
  447.     v = VarFind (name, ctxt, (ctxt == VAR_GLOBAL) ? FIND_ENV : 0);
  448.  
  449.     if (v == (Var *) NIL) {
  450.     VarAdd (name, val, ctxt);
  451.     } else {
  452.     Buf_AddByte(v->val, (Byte)' ');
  453.     Buf_AddBytes(v->val, strlen(val), (Byte *)val);
  454.  
  455.     if (DEBUG(VAR)) {
  456.         printf("%s:%s = %s\n", ctxt->name, name,
  457.            Buf_GetAll(v->val, (int *)NULL));
  458.     }
  459.  
  460.     if (v->flags & VAR_FROM_ENV) {
  461.         /*
  462.          * If the original variable came from the environment, we
  463.          * have to install it in the global context (we could place
  464.          * it in the environment, but then we should provide a way to
  465.          * export other variables...)
  466.          */
  467.         v->flags &= ~VAR_FROM_ENV;
  468.         Lst_AtFront(ctxt->context, (ClientData)v);
  469.     }
  470.     }
  471. }
  472.  
  473. /*-
  474.  *-----------------------------------------------------------------------
  475.  * Var_Exists --
  476.  *    See if the given variable exists.
  477.  *
  478.  * Results:
  479.  *    TRUE if it does, FALSE if it doesn't
  480.  *
  481.  * Side Effects:
  482.  *    None.
  483.  *
  484.  *-----------------------------------------------------------------------
  485.  */
  486. Boolean
  487. Var_Exists(name, ctxt)
  488.     char      *name;        /* Variable to find */
  489.     GNode      *ctxt;        /* Context in which to start search */
  490. {
  491.     Var              *v;
  492.  
  493.     v = VarFind(name, ctxt, FIND_CMD|FIND_GLOBAL|FIND_ENV);
  494.  
  495.     if (v == (Var *)NIL) {
  496.     return(FALSE);
  497.     } else if (v->flags & VAR_FROM_ENV) {
  498.     Buf_Destroy(v->val, TRUE);
  499.     free((char *)v);
  500.     }
  501.     return(TRUE);
  502. }
  503.  
  504. /*-
  505.  *-----------------------------------------------------------------------
  506.  * Var_Value --
  507.  *    Return the value of the named variable in the given context
  508.  *
  509.  * Results:
  510.  *    The value if the variable exists, NULL if it doesn't
  511.  *
  512.  * Side Effects:
  513.  *    None
  514.  *-----------------------------------------------------------------------
  515.  */
  516. char *
  517. Var_Value (name, ctxt)
  518.     char           *name;    /* name to find */
  519.     GNode          *ctxt;    /* context in which to search for it */
  520. {
  521.     Var            *v;
  522.  
  523.     v = VarFind (name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
  524.     if (v != (Var *) NIL) {
  525.     return ((char *)Buf_GetAll(v->val, (int *)NULL));
  526.     } else {
  527.     return ((char *) NULL);
  528.     }
  529. }
  530.  
  531. /*-
  532.  *-----------------------------------------------------------------------
  533.  * VarHead --
  534.  *    Remove the tail of the given word and place the result in the given
  535.  *    buffer.
  536.  *
  537.  * Results:
  538.  *    TRUE if characters were added to the buffer (a space needs to be
  539.  *    added to the buffer before the next word).
  540.  *
  541.  * Side Effects:
  542.  *    The trimmed word is added to the buffer.
  543.  *
  544.  *-----------------------------------------------------------------------
  545.  */
  546. static Boolean
  547. VarHead (word, addSpace, buf)
  548.     char          *word;        /* Word to trim */
  549.     Boolean       addSpace;     /* True if need to add a space to the buffer
  550.                  * before sticking in the head */
  551.     Buffer        buf;            /* Buffer in which to store it */
  552. {
  553.     register char *slash;
  554.  
  555.     slash = rindex (word, '/');
  556.     if (slash != (char *)NULL) {
  557.     if (addSpace) {
  558.         Buf_AddByte (buf, (Byte)' ');
  559.     }
  560.     *slash = '\0';
  561.     Buf_AddBytes (buf, strlen (word), (Byte *)word);
  562.     *slash = '/';
  563.     return (TRUE);
  564.     } else {
  565.     /*
  566.      * If no directory part, give . (q.v. the POSIX standard)
  567.      */
  568.     if (addSpace) {
  569.         Buf_AddBytes(buf, 2, (Byte *)" .");
  570.     } else {
  571.         Buf_AddByte(buf, (Byte)'.');
  572.     }
  573.     return(TRUE);
  574.     }
  575. }
  576.  
  577. /*-
  578.  *-----------------------------------------------------------------------
  579.  * VarTail --
  580.  *    Remove the head of the given word and place the result in the given
  581.  *    buffer.
  582.  *
  583.  * Results:
  584.  *    TRUE if characters were added to the buffer (a space needs to be
  585.  *    added to the buffer before the next word).
  586.  *
  587.  * Side Effects:
  588.  *    The trimmed word is added to the buffer.
  589.  *
  590.  *-----------------------------------------------------------------------
  591.  */
  592. static Boolean
  593. VarTail (word, addSpace, buf)
  594.     char          *word;        /* Word to trim */
  595.     Boolean       addSpace;     /* TRUE if need to stick a space in the
  596.                  * buffer before adding the tail */
  597.     Buffer        buf;            /* Buffer in which to store it */
  598. {
  599.     register char *slash;
  600.  
  601.     if (addSpace) {
  602.     Buf_AddByte (buf, (Byte)' ');
  603.     }
  604.  
  605.     slash = rindex (word, '/');
  606.     if (slash != (char *)NULL) {
  607.     *slash++ = '\0';
  608.     Buf_AddBytes (buf, strlen(slash), (Byte *)slash);
  609.     slash[-1] = '/';
  610.     } else {
  611.     Buf_AddBytes (buf, strlen(word), (Byte *)word);
  612.     }
  613.     return (TRUE);
  614. }
  615.  
  616. /*-
  617.  *-----------------------------------------------------------------------
  618.  * VarSuffix --
  619.  *    Place the suffix of the given word in the given buffer.
  620.  *
  621.  * Results:
  622.  *    TRUE if characters were added to the buffer (a space needs to be
  623.  *    added to the buffer before the next word).
  624.  *
  625.  * Side Effects:
  626.  *    The suffix from the word is placed in the buffer.
  627.  *
  628.  *-----------------------------------------------------------------------
  629.  */
  630. static Boolean
  631. VarSuffix (word, addSpace, buf)
  632.     char          *word;        /* Word to trim */
  633.     Boolean       addSpace;     /* TRUE if need to add a space before placing
  634.                  * the suffix in the buffer */
  635.     Buffer        buf;            /* Buffer in which to store it */
  636. {
  637.     register char *dot;
  638.  
  639.     dot = rindex (word, '.');
  640.     if (dot != (char *)NULL) {
  641.     if (addSpace) {
  642.         Buf_AddByte (buf, (Byte)' ');
  643.     }
  644.     *dot++ = '\0';
  645.     Buf_AddBytes (buf, strlen (dot), (Byte *)dot);
  646.     dot[-1] = '.';
  647.     return (TRUE);
  648.     } else {
  649.     return (addSpace);
  650.     }
  651. }
  652.  
  653. /*-
  654.  *-----------------------------------------------------------------------
  655.  * VarRoot --
  656.  *    Remove the suffix of the given word and place the result in the
  657.  *    buffer.
  658.  *
  659.  * Results:
  660.  *    TRUE if characters were added to the buffer (a space needs to be
  661.  *    added to the buffer before the next word).
  662.  *
  663.  * Side Effects:
  664.  *    The trimmed word is added to the buffer.
  665.  *
  666.  *-----------------------------------------------------------------------
  667.  */
  668. static Boolean
  669. VarRoot (word, addSpace, buf)
  670.     char          *word;        /* Word to trim */
  671.     Boolean       addSpace;     /* TRUE if need to add a space to the buffer
  672.                  * before placing the root in it */
  673.     Buffer        buf;            /* Buffer in which to store it */
  674. {
  675.     register char *dot;
  676.  
  677.     if (addSpace) {
  678.     Buf_AddByte (buf, (Byte)' ');
  679.     }
  680.  
  681.     dot = rindex (word, '.');
  682.     if (dot != (char *)NULL) {
  683.     *dot = '\0';
  684.     Buf_AddBytes (buf, strlen (word), (Byte *)word);
  685.     *dot = '.';
  686.     } else {
  687.     Buf_AddBytes (buf, strlen(word), (Byte *)word);
  688.     }
  689.     return (TRUE);
  690. }
  691.  
  692. /*-
  693.  *-----------------------------------------------------------------------
  694.  * VarMatch --
  695.  *    Place the word in the buffer if it matches the given pattern.
  696.  *    Callback function for VarModify to implement the :M modifier.
  697.  *    
  698.  * Results:
  699.  *    TRUE if a space should be placed in the buffer before the next
  700.  *    word.
  701.  *
  702.  * Side Effects:
  703.  *    The word may be copied to the buffer.
  704.  *
  705.  *-----------------------------------------------------------------------
  706.  */
  707. static Boolean
  708. VarMatch (word, addSpace, buf, pattern)
  709.     char          *word;        /* Word to examine */
  710.     Boolean       addSpace;     /* TRUE if need to add a space to the
  711.                  * buffer before adding the word, if it
  712.                  * matches */
  713.     Buffer        buf;            /* Buffer in which to store it */
  714.     char          *pattern;     /* Pattern the word must match */
  715. {
  716.     if (Str_Match(word, pattern)) {
  717.     if (addSpace) {
  718.         Buf_AddByte(buf, (Byte)' ');
  719.     }
  720.     addSpace = TRUE;
  721.     Buf_AddBytes(buf, strlen(word), (Byte *)word);
  722.     }
  723.     return(addSpace);
  724. }
  725.  
  726. /*-
  727.  *-----------------------------------------------------------------------
  728.  * VarNoMatch --
  729.  *    Place the word in the buffer if it doesn't match the given pattern.
  730.  *    Callback function for VarModify to implement the :N modifier.
  731.  *    
  732.  * Results:
  733.  *    TRUE if a space should be placed in the buffer before the next
  734.  *    word.
  735.  *
  736.  * Side Effects:
  737.  *    The word may be copied to the buffer.
  738.  *
  739.  *-----------------------------------------------------------------------
  740.  */
  741. static Boolean
  742. VarNoMatch (word, addSpace, buf, pattern)
  743.     char          *word;        /* Word to examine */
  744.     Boolean       addSpace;     /* TRUE if need to add a space to the
  745.                  * buffer before adding the word, if it
  746.                  * matches */
  747.     Buffer        buf;            /* Buffer in which to store it */
  748.     char          *pattern;     /* Pattern the word must match */
  749. {
  750.     if (!Str_Match(word, pattern)) {
  751.     if (addSpace) {
  752.         Buf_AddByte(buf, (Byte)' ');
  753.     }
  754.     addSpace = TRUE;
  755.     Buf_AddBytes(buf, strlen(word), (Byte *)word);
  756.     }
  757.     return(addSpace);
  758. }
  759.  
  760. typedef struct {
  761.     char          *lhs;        /* String to match */
  762.     int              leftLen;  /* Length of string */
  763.     char          *rhs;        /* Replacement string (w/ &'s removed) */
  764.     int              rightLen; /* Length of replacement */
  765.     int              flags;
  766. #define VAR_SUB_GLOBAL    1   /* Apply substitution globally */
  767. #define VAR_MATCH_START    2   /* Match at start of word */
  768. #define VAR_MATCH_END    4   /* Match at end of word */
  769. #define VAR_NO_SUB    8   /* Substitution is non-global and already done */
  770. } VarPattern;
  771.  
  772. /*-
  773.  *-----------------------------------------------------------------------
  774.  * VarSubstitute --
  775.  *    Perform a string-substitution on the given word, placing the
  776.  *    result in the passed buffer.
  777.  *
  778.  * Results:
  779.  *    TRUE if a space is needed before more characters are added.
  780.  *
  781.  * Side Effects:
  782.  *    None.
  783.  *
  784.  *-----------------------------------------------------------------------
  785.  */
  786. static Boolean
  787. VarSubstitute (word, addSpace, buf, pattern)
  788.     char              *word;        /* Word to modify */
  789.     Boolean           addSpace;   /* True if space should be added before
  790.                      * other characters */
  791.     Buffer            buf;        /* Buffer for result */
  792.     register VarPattern    *pattern;   /* Pattern for substitution */
  793. {
  794.     register int      wordLen;    /* Length of word */
  795.     register char     *cp;        /* General pointer */
  796.  
  797.     wordLen = strlen(word);
  798.     if ((pattern->flags & VAR_NO_SUB) == 0) {
  799.     /*
  800.      * Still substituting -- break it down into simple anchored cases
  801.      * and if none of them fits, perform the general substitution case.
  802.      */
  803.     if ((pattern->flags & VAR_MATCH_START) &&
  804.         (strncmp(word, pattern->lhs, pattern->leftLen) == 0)) {
  805.         /*
  806.          * Anchored at start and beginning of word matches pattern
  807.          */
  808.         if ((pattern->flags & VAR_MATCH_END) &&
  809.             (wordLen == pattern->leftLen)) {
  810.             /*
  811.              * Also anchored at end and matches to the end (word
  812.              * is same length as pattern) add space and rhs only
  813.              * if rhs is non-null.
  814.              */
  815.             if (pattern->rightLen != 0) {
  816.                 if (addSpace) {
  817.                 Buf_AddByte(buf, (Byte)' ');
  818.                 }
  819.                 addSpace = TRUE;
  820.                 Buf_AddBytes(buf, pattern->rightLen,
  821.                      (Byte *)pattern->rhs);
  822.             }
  823.         } else if (pattern->flags & VAR_MATCH_END) {
  824.             /*
  825.              * Doesn't match to end -- copy word wholesale
  826.              */
  827.             goto nosub;
  828.         } else {
  829.             /*
  830.              * Matches at start but need to copy in trailing characters
  831.              */
  832.             if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){
  833.             if (addSpace) {
  834.                 Buf_AddByte(buf, (Byte)' ');
  835.             }
  836.             addSpace = TRUE;
  837.             }
  838.             Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);
  839.             Buf_AddBytes(buf, wordLen - pattern->leftLen,
  840.                  (Byte *)(word + pattern->leftLen));
  841.         }
  842.     } else if (pattern->flags & VAR_MATCH_START) {
  843.         /*
  844.          * Had to match at start of word and didn't -- copy whole word.
  845.          */
  846.         goto nosub;
  847.     } else if (pattern->flags & VAR_MATCH_END) {
  848.         /*
  849.          * Anchored at end, Find only place match could occur (leftLen
  850.          * characters from the end of the word) and see if it does. Note
  851.          * that because the $ will be left at the end of the lhs, we have
  852.          * to use strncmp.
  853.          */
  854.         cp = word + (wordLen - pattern->leftLen);
  855.         if ((cp >= word) &&
  856.         (strncmp(cp, pattern->lhs, pattern->leftLen) == 0)) {
  857.         /*
  858.          * Match found. If we will place characters in the buffer,
  859.          * add a space before hand as indicated by addSpace, then
  860.          * stuff in the initial, unmatched part of the word followed
  861.          * by the right-hand-side.
  862.          */
  863.         if (((cp - word) + pattern->rightLen) != 0) {
  864.             if (addSpace) {
  865.             Buf_AddByte(buf, (Byte)' ');
  866.             }
  867.             addSpace = TRUE;
  868.         }
  869.         Buf_AddBytes(buf, cp - word, (Byte *)word);
  870.         Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);
  871.         } else {
  872.         /*
  873.          * Had to match at end and didn't. Copy entire word.
  874.          */
  875.         goto nosub;
  876.         }
  877.     } else {
  878.         /*
  879.          * Pattern is unanchored: search for the pattern in the word using
  880.          * String_FindSubstring, copying unmatched portions and the
  881.          * right-hand-side for each match found, handling non-global
  882.          * subsititutions correctly, etc. When the loop is done, any
  883.          * remaining part of the word (word and wordLen are adjusted
  884.          * accordingly through the loop) is copied straight into the
  885.          * buffer.
  886.          * addSpace is set FALSE as soon as a space is added to the
  887.          * buffer.
  888.          */
  889.         register Boolean done;
  890.         int origSize;
  891.  
  892.         done = FALSE;
  893.         origSize = Buf_Size(buf);
  894.         while (!done) {
  895.         cp = Str_FindSubstring(word, pattern->lhs);
  896.         if (cp != (char *)NULL) {
  897.             if (addSpace && (((cp - word) + pattern->rightLen) != 0)){
  898.             Buf_AddByte(buf, (Byte)' ');
  899.             addSpace = FALSE;
  900.             }
  901.             Buf_AddBytes(buf, cp-word, (Byte *)word);
  902.             Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);
  903.             wordLen -= (cp - word) + pattern->leftLen;
  904.             word = cp + pattern->leftLen;
  905.             if (wordLen == 0) {
  906.             done = TRUE;
  907.             }
  908.             if ((pattern->flags & VAR_SUB_GLOBAL) == 0) {
  909.             done = TRUE;
  910.             pattern->flags |= VAR_NO_SUB;
  911.             }
  912.         } else {
  913.             done = TRUE;
  914.         }
  915.         }
  916.         if (wordLen != 0) {
  917.         if (addSpace) {
  918.             Buf_AddByte(buf, (Byte)' ');
  919.         }
  920.         Buf_AddBytes(buf, wordLen, (Byte *)word);
  921.         }
  922.         /*
  923.          * If added characters to the buffer, need to add a space
  924.          * before we add any more. If we didn't add any, just return
  925.          * the previous value of addSpace.
  926.          */
  927.         return ((Buf_Size(buf) != origSize) || addSpace);
  928.     }
  929.     /*
  930.      * Common code for anchored substitutions: if performed a substitution
  931.      * and it's not supposed to be global, mark the pattern as requiring
  932.      * no more substitutions. addSpace was set TRUE if characters were
  933.      * added to the buffer.
  934.      */
  935.     if ((pattern->flags & VAR_SUB_GLOBAL) == 0) {
  936.         pattern->flags |= VAR_NO_SUB;
  937.     }
  938.     return (addSpace);
  939.     }
  940.  nosub:
  941.     if (addSpace) {
  942.     Buf_AddByte(buf, (Byte)' ');
  943.     }
  944.     Buf_AddBytes(buf, wordLen, (Byte *)word);
  945.     return(TRUE);
  946. }
  947.  
  948. /*-
  949.  *-----------------------------------------------------------------------
  950.  * VarModify --
  951.  *    Modify each of the words of the passed string using the given
  952.  *    function. Used to implement all modifiers.
  953.  *
  954.  * Results:
  955.  *    A string of all the words modified appropriately.
  956.  *
  957.  * Side Effects:
  958.  *    None.
  959.  *
  960.  *-----------------------------------------------------------------------
  961.  */
  962. static char *
  963. VarModify (str, modProc, datum)
  964.     char          *str;                /* String whose words should be trimmed */
  965.     Boolean          (*modProc)();     /* Function to use to modify them */
  966.     ClientData      datum;            /* Datum to pass it */
  967. {
  968.     Buffer        buf;                /* Buffer for the new string */
  969.     register char *cp;                /* Pointer to end of current word */
  970.     char          endc;                /* Character that ended the word */
  971.     Boolean       addSpace;         /* TRUE if need to add a space to the
  972.                      * buffer before adding the trimmed
  973.                      * word */
  974.     
  975.     buf = Buf_Init (0);
  976.     cp = str;
  977.     addSpace = FALSE;
  978.     
  979.     while (1) {
  980.     /*
  981.      * Skip to next word and place cp at its end.
  982.      */
  983.     while (isspace (*str)) {
  984.         str++;
  985.     }
  986.     for (cp = str; *cp != '\0' && !isspace (*cp); cp++) {
  987.         /* void */ ;
  988.     }
  989.     if (cp == str) {
  990.         /*
  991.          * If we didn't go anywhere, we must be done!
  992.          */
  993.         Buf_AddByte (buf, '\0');
  994.         str = (char *)Buf_GetAll (buf, (int *)NULL);
  995.         Buf_Destroy (buf, FALSE);
  996.         return (str);
  997.     }
  998.     /*
  999.      * Nuke terminating character, but save it in endc b/c if str was
  1000.      * some variable's value, it would not be good to screw it
  1001.      * over...
  1002.      */
  1003.     endc = *cp;
  1004.     *cp = '\0';
  1005.  
  1006.     addSpace = (* modProc) (str, addSpace, buf, datum);
  1007.  
  1008.     if (endc) {
  1009.         *cp++ = endc;
  1010.     }
  1011.     str = cp;
  1012.     }
  1013. }
  1014.  
  1015. /*-
  1016.  *-----------------------------------------------------------------------
  1017.  * Var_Parse --
  1018.  *    Given the start of a variable invocation, extract the variable
  1019.  *    name and find its value, then modify it according to the
  1020.  *    specification.
  1021.  *
  1022.  * Results:
  1023.  *    The (possibly-modified) value of the variable or var_Error if the
  1024.  *    specification is invalid. The length of the specification is
  1025.  *    placed in *lengthPtr (for invalid specifications, this is just
  1026.  *    2...?).
  1027.  *    A Boolean in *freePtr telling whether the returned string should
  1028.  *    be freed by the caller.
  1029.  *
  1030.  * Side Effects:
  1031.  *    None.
  1032.  *
  1033.  *-----------------------------------------------------------------------
  1034.  */
  1035. char *
  1036. Var_Parse (str, ctxt, err, lengthPtr, freePtr)
  1037.     char          *str;            /* The string to parse */
  1038.     GNode         *ctxt;        /* The context for the variable */
  1039.     Boolean         err;        /* TRUE if undefined variables are an error */
  1040.     int                *lengthPtr;    /* OUT: The length of the specification */
  1041.     Boolean         *freePtr;     /* OUT: TRUE if caller should free result */
  1042. {
  1043.     register char   *tstr;        /* Pointer into str */
  1044.     Var                *v;            /* Variable in invocation */
  1045.     register char   *cp;        /* Secondary pointer into str (place marker
  1046.                  * for tstr) */
  1047.     Boolean         haveModifier;/* TRUE if have modifiers for the variable */
  1048.     register char   endc;        /* Ending character when variable in parens
  1049.                  * or braces */
  1050.     char            *start;
  1051.     Boolean         dynamic;    /* TRUE if the variable is local and we're
  1052.                  * expanding it in a non-local context. This
  1053.                  * is done to support dynamic sources. The
  1054.                  * result is just the invocation, unaltered */
  1055.     
  1056.     *freePtr = FALSE;
  1057.     dynamic = FALSE;
  1058.     start = str;
  1059.     
  1060.     if (str[1] != '(' && str[1] != '{') {
  1061.     /*
  1062.      * If it's not bounded by braces of some sort, life is much simpler.
  1063.      * We just need to check for the first character and return the
  1064.      * value if it exists.
  1065.      */
  1066.     char      name[2];
  1067.  
  1068.     name[0] = str[1];
  1069.     name[1] = '\0';
  1070.  
  1071.     v = VarFind (name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
  1072.     if (v == (Var *)NIL) {
  1073.         *lengthPtr = 2;
  1074.         
  1075.         if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) {
  1076.         /*
  1077.          * If substituting a local variable in a non-local context,
  1078.          * assume it's for dynamic source stuff. We have to handle
  1079.          * this specially and return the longhand for the variable
  1080.          * with the dollar sign escaped so it makes it back to the
  1081.          * caller. Only four of the local variables are treated
  1082.          * specially as they are the only four that will be set
  1083.          * when dynamic sources are expanded.
  1084.          */
  1085.         switch (str[1]) {
  1086.             case '@':
  1087.             return("$(.TARGET)");
  1088.             case '%':
  1089.             return("$(.ARCHIVE)");
  1090.             case '*':
  1091.             return("$(.PREFIX)");
  1092.             case '!':
  1093.             return("$(.MEMBER)");
  1094.         }
  1095.         }
  1096.         /*
  1097.          * Error
  1098.          */
  1099.         return (err ? var_Error : varNoError);
  1100.     } else {
  1101.         haveModifier = FALSE;
  1102.         tstr = &str[1];
  1103.         endc = str[1];
  1104.     }
  1105.     } else {
  1106.     endc = str[1] == '(' ? ')' : '}';
  1107.  
  1108.     /*
  1109.      * Skip to the end character or a colon, whichever comes first.
  1110.      */
  1111.     for (tstr = str + 2;
  1112.          *tstr != '\0' && *tstr != endc && *tstr != ':';
  1113.          tstr++)
  1114.     {
  1115.         continue;
  1116.     }
  1117.     if (*tstr == ':') {
  1118.         haveModifier = TRUE;
  1119.     } else if (*tstr != '\0') {
  1120.         haveModifier = FALSE;
  1121.     } else {
  1122.         /*
  1123.          * If we never did find the end character, return NULL
  1124.          * right now, setting the length to be the distance to
  1125.          * the end of the string, since that's what make does.
  1126.          */
  1127.         *lengthPtr = tstr - str;
  1128.         return (var_Error);
  1129.     }
  1130.     *tstr = '\0';
  1131.      
  1132.     v = VarFind (str + 2, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
  1133.     if ((v == (Var *)NIL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) &&
  1134.         ((tstr-str) == 4) && (str[3] == 'F' || str[3] == 'D'))
  1135.     {
  1136.         /*
  1137.          * Check for bogus D and F forms of local variables since we're
  1138.          * in a local context and the name is the right length.
  1139.          */
  1140.         switch(str[2]) {
  1141.         case '@':
  1142.         case '%':
  1143.         case '*':
  1144.         case '!':
  1145.         case '>':
  1146.         case '<':
  1147.         {
  1148.             char    vname[2];
  1149.             char    *val;
  1150.  
  1151.             /*
  1152.              * Well, it's local -- go look for it.
  1153.              */
  1154.             vname[0] = str[2];
  1155.             vname[1] = '\0';
  1156.             v = VarFind(vname, ctxt, 0);
  1157.             
  1158.             if (v != (Var *)NIL) {
  1159.             /*
  1160.              * No need for nested expansion or anything, as we're
  1161.              * the only one who sets these things and we sure don't
  1162.              * but nested invocations in them...
  1163.              */
  1164.             val = (char *)Buf_GetAll(v->val, (int *)NULL);
  1165.             
  1166.             if (str[3] == 'D') {
  1167.                 val = VarModify(val, VarHead, (ClientData)0);
  1168.             } else {
  1169.                 val = VarModify(val, VarTail, (ClientData)0);
  1170.             }
  1171.             /*
  1172.              * Resulting string is dynamically allocated, so
  1173.              * tell caller to free it.
  1174.              */
  1175.             *freePtr = TRUE;
  1176.             *lengthPtr = tstr-start+1;
  1177.             *tstr = endc;
  1178.             return(val);
  1179.             }
  1180.             break;
  1181.         }
  1182.         }
  1183.     }
  1184.                 
  1185.     if (v == (Var *)NIL) {
  1186.         if ((((tstr-str) == 3) ||
  1187.          ((((tstr-str) == 4) && (str[3] == 'F' ||
  1188.                      str[3] == 'D')))) &&
  1189.         ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)))
  1190.         {
  1191.         /*
  1192.          * If substituting a local variable in a non-local context,
  1193.          * assume it's for dynamic source stuff. We have to handle
  1194.          * this specially and return the longhand for the variable
  1195.          * with the dollar sign escaped so it makes it back to the
  1196.          * caller. Only four of the local variables are treated
  1197.          * specially as they are the only four that will be set
  1198.          * when dynamic sources are expanded.
  1199.          */
  1200.         switch (str[2]) {
  1201.             case '@':
  1202.             case '%':
  1203.             case '*':
  1204.             case '!':
  1205.             dynamic = TRUE;
  1206.             break;
  1207.         }
  1208.         } else if (((tstr-str) > 4) && (str[2] == '.') &&
  1209.                isupper(str[3]) &&
  1210.                ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)))
  1211.         {
  1212.         int    len;
  1213.         
  1214.         len = (tstr-str) - 3;
  1215.         if ((strncmp(str+2, ".TARGET", len) == 0) ||
  1216.             (strncmp(str+2, ".ARCHIVE", len) == 0) ||
  1217.             (strncmp(str+2, ".PREFIX", len) == 0) ||
  1218.             (strncmp(str+2, ".MEMBER", len) == 0))
  1219.         {
  1220.             dynamic = TRUE;
  1221.         }
  1222.         }
  1223.         
  1224.         if (!haveModifier) {
  1225.         /*
  1226.          * No modifiers -- have specification length so we can return
  1227.          * now.
  1228.          */
  1229.         *lengthPtr = tstr - start + 1;
  1230.         *tstr = endc;
  1231.         if (dynamic) {
  1232.             str = emalloc(*lengthPtr + 1);
  1233.             strncpy(str, start, *lengthPtr);
  1234.             str[*lengthPtr] = '\0';
  1235.             *freePtr = TRUE;
  1236.             return(str);
  1237.         } else {
  1238.             return (err ? var_Error : varNoError);
  1239.         }
  1240.         } else {
  1241.         /*
  1242.          * Still need to get to the end of the variable specification,
  1243.          * so kludge up a Var structure for the modifications
  1244.          */
  1245.         v = (Var *) emalloc(sizeof(Var));
  1246.         v->name = &str[1];
  1247.         v->val = Buf_Init(1);
  1248.         v->flags = VAR_JUNK;
  1249.         }
  1250.     }
  1251.     }
  1252.  
  1253.     if (v->flags & VAR_IN_USE) {
  1254.     Fatal("Variable %s is recursive.", v->name);
  1255.     /*NOTREACHED*/
  1256.     } else {
  1257.     v->flags |= VAR_IN_USE;
  1258.     }
  1259.     /*
  1260.      * Before doing any modification, we have to make sure the value
  1261.      * has been fully expanded. If it looks like recursion might be
  1262.      * necessary (there's a dollar sign somewhere in the variable's value)
  1263.      * we just call Var_Subst to do any other substitutions that are
  1264.      * necessary. Note that the value returned by Var_Subst will have
  1265.      * been dynamically-allocated, so it will need freeing when we
  1266.      * return.
  1267.      */
  1268.     str = (char *)Buf_GetAll(v->val, (int *)NULL);
  1269.     if (index (str, '$') != (char *)NULL) {
  1270.     str = Var_Subst(str, ctxt, err);
  1271.     *freePtr = TRUE;
  1272.     }
  1273.     
  1274.     v->flags &= ~VAR_IN_USE;
  1275.     
  1276.     /*
  1277.      * Now we need to apply any modifiers the user wants applied.
  1278.      * These are:
  1279.      *        :M<pattern>    words which match the given <pattern>.
  1280.      *                    <pattern> is of the standard file
  1281.      *                    wildcarding form.
  1282.      *        :S<d><pat1><d><pat2><d>[g]
  1283.      *                    Substitute <pat2> for <pat1> in the value
  1284.      *        :H            Substitute the head of each word
  1285.      *        :T            Substitute the tail of each word
  1286.      *        :E            Substitute the extension (minus '.') of
  1287.      *                    each word
  1288.      *        :R            Substitute the root of each word
  1289.      *                    (pathname minus the suffix).
  1290.      *              :lhs=rhs      Like :S, but the rhs goes to the end of
  1291.      *                            the invocation.
  1292.      */
  1293.     if ((str != (char *)NULL) && haveModifier) {
  1294.     /*
  1295.      * Skip initial colon while putting it back.
  1296.      */
  1297.     *tstr++ = ':';
  1298.     while (*tstr != endc) {
  1299.         char    *newStr;    /* New value to return */
  1300.         char    termc;        /* Character which terminated scan */
  1301.         
  1302.         if (DEBUG(VAR)) {
  1303.         printf("Applying :%c to \"%s\"\n", *tstr, str);
  1304.         }
  1305.         switch (*tstr) {
  1306.         case 'N':
  1307.         case 'M':
  1308.         {
  1309.             char    *pattern;
  1310.             char    *cp2;
  1311.             Boolean copy;
  1312.  
  1313.             copy = FALSE;
  1314.             for (cp = tstr + 1;
  1315.              *cp != '\0' && *cp != ':' && *cp != endc;
  1316.              cp++)
  1317.             {
  1318.             if (*cp == '\\' && (cp[1] == ':' || cp[1] == endc)){
  1319.                 copy = TRUE;
  1320.                 cp++;
  1321.             }
  1322.             }
  1323.             termc = *cp;
  1324.             *cp = '\0';
  1325.             if (copy) {
  1326.             /*
  1327.              * Need to compress the \:'s out of the pattern, so
  1328.              * allocate enough room to hold the uncompressed
  1329.              * pattern (note that cp started at tstr+1, so
  1330.              * cp - tstr takes the null byte into account) and
  1331.              * compress the pattern into the space.
  1332.              */
  1333.             pattern = emalloc(cp - tstr);
  1334.             for (cp2 = pattern, cp = tstr + 1;
  1335.                  *cp != '\0';
  1336.                  cp++, cp2++)
  1337.             {
  1338.                 if ((*cp == '\\') &&
  1339.                 (cp[1] == ':' || cp[1] == endc)) {
  1340.                     cp++;
  1341.                 }
  1342.                 *cp2 = *cp;
  1343.             }
  1344.             *cp2 = '\0';
  1345.             } else {
  1346.             pattern = &tstr[1];
  1347.             }
  1348.             if (*tstr == 'M' || *tstr == 'm') {
  1349.             newStr = VarModify(str, VarMatch, (ClientData)pattern);
  1350.             } else {
  1351.             newStr = VarModify(str, VarNoMatch,
  1352.                        (ClientData)pattern);
  1353.             }
  1354.             if (copy) {
  1355.             free(pattern);
  1356.             }
  1357.             break;
  1358.         }
  1359.         case 'S':
  1360.         {
  1361.             VarPattern         pattern;
  1362.             register char   delim;
  1363.             Buffer          buf;        /* Buffer for patterns */
  1364.             register char   *cp2;
  1365.             int                lefts;
  1366.  
  1367.             pattern.flags = 0;
  1368.             delim = tstr[1];
  1369.             tstr += 2;
  1370.             /*
  1371.              * If pattern begins with '^', it is anchored to the
  1372.              * start of the word -- skip over it and flag pattern.
  1373.              */
  1374.             if (*tstr == '^') {
  1375.             pattern.flags |= VAR_MATCH_START;
  1376.             tstr += 1;
  1377.             }
  1378.  
  1379.             buf = Buf_Init(0);
  1380.             
  1381.             /*
  1382.              * Pass through the lhs looking for 1) escaped delimiters,
  1383.              * '$'s and backslashes (place the escaped character in
  1384.              * uninterpreted) and 2) unescaped $'s that aren't before
  1385.              * the delimiter (expand the variable substitution).
  1386.              * The result is left in the Buffer buf.
  1387.              */
  1388.             for (cp = tstr; *cp != '\0' && *cp != delim; cp++) {
  1389.             if ((*cp == '\\') &&
  1390.                 ((cp[1] == delim) ||
  1391.                  (cp[1] == '$') ||
  1392.                  (cp[1] == '\\')))
  1393.             {
  1394.                 Buf_AddByte(buf, (Byte)cp[1]);
  1395.                 cp++;
  1396.             } else if (*cp == '$') {
  1397.                 if (cp[1] != delim) {
  1398.                 /*
  1399.                  * If unescaped dollar sign not before the
  1400.                  * delimiter, assume it's a variable
  1401.                  * substitution and recurse.
  1402.                  */
  1403.                 char        *cp2;
  1404.                 int        len;
  1405.                 Boolean        freeIt;
  1406.                 
  1407.                 cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt);
  1408.                 Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2);
  1409.                 if (freeIt) {
  1410.                     free(cp2);
  1411.                 }
  1412.                 cp += len - 1;
  1413.                 } else {
  1414.                 /*
  1415.                  * Unescaped $ at end of pattern => anchor
  1416.                  * pattern at end.
  1417.                  */
  1418.                 pattern.flags |= VAR_MATCH_END;
  1419.                 }
  1420.             } else {
  1421.                 Buf_AddByte(buf, (Byte)*cp);
  1422.             }
  1423.             }
  1424.  
  1425.             Buf_AddByte(buf, (Byte)'\0');
  1426.             
  1427.             /*
  1428.              * If lhs didn't end with the delimiter, complain and
  1429.              * return NULL
  1430.              */
  1431.             if (*cp != delim) {
  1432.             *lengthPtr = cp - start + 1;
  1433.             if (*freePtr) {
  1434.                 free(str);
  1435.             }
  1436.             Buf_Destroy(buf, TRUE);
  1437.             Error("Unclosed substitution for %s (%c missing)",
  1438.                   v->name, delim);
  1439.             return (var_Error);
  1440.             }
  1441.  
  1442.             /*
  1443.              * Fetch pattern and destroy buffer, but preserve the data
  1444.              * in it, since that's our lhs. Note that Buf_GetAll
  1445.              * will return the actual number of bytes, which includes
  1446.              * the null byte, so we have to decrement the length by
  1447.              * one.
  1448.              */
  1449.             pattern.lhs = (char *)Buf_GetAll(buf, &pattern.leftLen);
  1450.             pattern.leftLen--;
  1451.             Buf_Destroy(buf, FALSE);
  1452.  
  1453.             /*
  1454.              * Now comes the replacement string. Three things need to
  1455.              * be done here: 1) need to compress escaped delimiters and
  1456.              * ampersands and 2) need to replace unescaped ampersands
  1457.              * with the l.h.s. (since this isn't regexp, we can do
  1458.              * it right here) and 3) expand any variable substitutions.
  1459.              */
  1460.             buf = Buf_Init(0);
  1461.             
  1462.             tstr = cp + 1;
  1463.             for (cp = tstr; *cp != '\0' && *cp != delim; cp++) {
  1464.             if ((*cp == '\\') &&
  1465.                 ((cp[1] == delim) ||
  1466.                  (cp[1] == '&') ||
  1467.                  (cp[1] == '\\') ||
  1468.                  (cp[1] == '$')))
  1469.             {
  1470.                 Buf_AddByte(buf, (Byte)cp[1]);
  1471.                 cp++;
  1472.             } else if ((*cp == '$') && (cp[1] != delim)) {
  1473.                 char    *cp2;
  1474.                 int        len;
  1475.                 Boolean freeIt;
  1476.  
  1477.                 cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt);
  1478.                 Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2);
  1479.                 cp += len - 1;
  1480.                 if (freeIt) {
  1481.                 free(cp2);
  1482.                 }
  1483.             } else if (*cp == '&') {
  1484.                 Buf_AddBytes(buf, pattern.leftLen,
  1485.                      (Byte *)pattern.lhs);
  1486.             } else {
  1487.                 Buf_AddByte(buf, (Byte)*cp);
  1488.             }
  1489.             }
  1490.  
  1491.             Buf_AddByte(buf, (Byte)'\0');
  1492.             
  1493.             /*
  1494.              * If didn't end in delimiter character, complain
  1495.              */
  1496.             if (*cp != delim) {
  1497.             *lengthPtr = cp - start + 1;
  1498.             if (*freePtr) {
  1499.                 free(str);
  1500.             }
  1501.             Buf_Destroy(buf, TRUE);
  1502.             Error("Unclosed substitution for %s (%c missing)",
  1503.                   v->name, delim);
  1504.             return (var_Error);
  1505.             }
  1506.  
  1507.             pattern.rhs = (char *)Buf_GetAll(buf, &pattern.rightLen);
  1508.             pattern.rightLen--;
  1509.             Buf_Destroy(buf, FALSE);
  1510.  
  1511.             /*
  1512.              * Check for global substitution. If 'g' after the final
  1513.              * delimiter, substitution is global and is marked that
  1514.              * way.
  1515.              */
  1516.             cp++;
  1517.             if (*cp == 'g') {
  1518.             pattern.flags |= VAR_SUB_GLOBAL;
  1519.             cp++;
  1520.             }
  1521.  
  1522.             termc = *cp;
  1523.             newStr = VarModify(str, VarSubstitute,
  1524.                        (ClientData)&pattern);
  1525.             /*
  1526.              * Free the two strings.
  1527.              */
  1528.             free(pattern.lhs);
  1529.             free(pattern.rhs);
  1530.             break;
  1531.         }
  1532.         case 'T':
  1533.             if (tstr[1] == endc || tstr[1] == ':') {
  1534.             newStr = VarModify (str, VarTail, (ClientData)0);
  1535.             cp = tstr + 1;
  1536.             termc = *cp;
  1537.             break;
  1538.             }
  1539.             /*FALLTHRU*/
  1540.         case 'H':
  1541.             if (tstr[1] == endc || tstr[1] == ':') {
  1542.             newStr = VarModify (str, VarHead, (ClientData)0);
  1543.             cp = tstr + 1;
  1544.             termc = *cp;
  1545.             break;
  1546.             }
  1547.             /*FALLTHRU*/
  1548.         case 'E':
  1549.             if (tstr[1] == endc || tstr[1] == ':') {
  1550.             newStr = VarModify (str, VarSuffix, (ClientData)0);
  1551.             cp = tstr + 1;
  1552.             termc = *cp;
  1553.             break;
  1554.             }
  1555.             /*FALLTHRU*/
  1556.         case 'R':
  1557.             if (tstr[1] == endc || tstr[1] == ':') {
  1558.             newStr = VarModify (str, VarRoot, (ClientData)0);
  1559.             cp = tstr + 1;
  1560.             termc = *cp;
  1561.             break;
  1562.             }
  1563.             /*FALLTHRU*/
  1564.         default: {
  1565.             /*
  1566.              * This can either be a bogus modifier or a System-V
  1567.              * substitution command.
  1568.              */
  1569.             VarPattern      pattern;
  1570.             Boolean         eqFound;
  1571.             
  1572.             pattern.flags = 0;
  1573.             eqFound = FALSE;
  1574.             /*
  1575.              * First we make a pass through the string trying
  1576.              * to verify it is a SYSV-make-style translation:
  1577.              * it must be: <string1>=<string2>)
  1578.              */
  1579.             for (cp = tstr; *cp != '\0' && *cp != endc; cp++) {
  1580.             if (*cp == '=') {
  1581.                 eqFound = TRUE;
  1582.                 /* continue looking for endc */
  1583.             }
  1584.             }
  1585.             if (*cp == endc && eqFound) {
  1586.             
  1587.             /*
  1588.              * Now we break this sucker into the lhs and
  1589.              * rhs. We must null terminate them of course.
  1590.              */
  1591.             for (cp = tstr; *cp != '='; cp++) {
  1592.                 ;
  1593.             }
  1594.             pattern.lhs = tstr;
  1595.             pattern.leftLen = cp - tstr;
  1596.             *cp++ = '\0';
  1597.             
  1598.             pattern.rhs = cp;
  1599.             while (*cp != endc) {
  1600.                 cp++;
  1601.             }
  1602.             pattern.rightLen = cp - pattern.rhs;
  1603.             *cp = '\0';
  1604.             
  1605.             /*
  1606.              * SYSV modifications happen through the whole
  1607.              * string. Note the pattern is anchored at the end.
  1608.              */
  1609.             pattern.flags |= VAR_SUB_GLOBAL|VAR_MATCH_END;
  1610.  
  1611.             newStr = VarModify(str, VarSubstitute,
  1612.                        (ClientData)&pattern);
  1613.  
  1614.             /*
  1615.              * Restore the nulled characters
  1616.              */
  1617.             pattern.lhs[pattern.leftLen] = '=';
  1618.             pattern.rhs[pattern.rightLen] = endc;
  1619.             termc = endc;
  1620.             } else {
  1621.             Error ("Unknown modifier '%c'\n", *tstr);
  1622.             for (cp = tstr+1;
  1623.                  *cp != ':' && *cp != endc && *cp != '\0';
  1624.                  cp++) {
  1625.                  ;
  1626.             }
  1627.             termc = *cp;
  1628.             newStr = var_Error;
  1629.             }
  1630.         }
  1631.         }
  1632.         if (DEBUG(VAR)) {
  1633.         printf("Result is \"%s\"\n", newStr);
  1634.         }
  1635.         
  1636.         if (*freePtr) {
  1637.         free (str);
  1638.         }
  1639.         str = newStr;
  1640.         if (str != var_Error) {
  1641.         *freePtr = TRUE;
  1642.         } else {
  1643.         *freePtr = FALSE;
  1644.         }
  1645.         if (termc == '\0') {
  1646.         Error("Unclosed variable specification for %s", v->name);
  1647.         } else if (termc == ':') {
  1648.         *cp++ = termc;
  1649.         } else {
  1650.         *cp = termc;
  1651.         }
  1652.         tstr = cp;
  1653.     }
  1654.     *lengthPtr = tstr - start + 1;
  1655.     } else {
  1656.     *lengthPtr = tstr - start + 1;
  1657.     *tstr = endc;
  1658.     }
  1659.     
  1660.     if (v->flags & VAR_FROM_ENV) {
  1661.     Boolean      destroy = FALSE;
  1662.     
  1663.     if (str != (char *)Buf_GetAll(v->val, (int *)NULL)) {
  1664.         destroy = TRUE;
  1665.     } else {
  1666.         /*
  1667.          * Returning the value unmodified, so tell the caller to free
  1668.          * the thing.
  1669.          */
  1670.         *freePtr = TRUE;
  1671.     }
  1672.     Buf_Destroy(v->val, destroy);
  1673.     free((Address)v);
  1674.     } else if (v->flags & VAR_JUNK) {
  1675.     /*
  1676.      * Perform any free'ing needed and set *freePtr to FALSE so the caller
  1677.      * doesn't try to free a static pointer.
  1678.      */
  1679.     if (*freePtr) {
  1680.         free(str);
  1681.     }
  1682.     *freePtr = FALSE;
  1683.     free((Address)v);
  1684.     if (dynamic) {
  1685.         str = emalloc(*lengthPtr + 1);
  1686.         strncpy(str, start, *lengthPtr);
  1687.         str[*lengthPtr] = '\0';
  1688.         *freePtr = TRUE;
  1689.     } else {
  1690.         str = var_Error;
  1691.     }
  1692.     }
  1693.     return (str);
  1694. }
  1695.  
  1696. /*-
  1697.  *-----------------------------------------------------------------------
  1698.  * Var_Subst  --
  1699.  *    Substitute for all variables in the given string in the given context
  1700.  *    If undefErr is TRUE, Parse_Error will be called when an undefined
  1701.  *    variable is encountered.
  1702.  *
  1703.  * Results:
  1704.  *    The resulting string.
  1705.  *
  1706.  * Side Effects:
  1707.  *    None. The old string must be freed by the caller
  1708.  *-----------------------------------------------------------------------
  1709.  */
  1710. char *
  1711. Var_Subst (str, ctxt, undefErr)
  1712.     register char *str;                /* the string in which to substitute */
  1713.     GNode         *ctxt;        /* the context wherein to find variables */
  1714.     Boolean       undefErr;         /* TRUE if undefineds are an error */
  1715. {
  1716.     Buffer        buf;                /* Buffer for forming things */
  1717.     char          *val;            /* Value to substitute for a variable */
  1718.     int              length;           /* Length of the variable invocation */
  1719.     Boolean       doFree;           /* Set true if val should be freed */
  1720.     static Boolean errorReported;   /* Set true if an error has already
  1721.                      * been reported to prevent a plethora
  1722.                      * of messages when recursing */
  1723.  
  1724.     buf = Buf_Init (BSIZE);
  1725.     errorReported = FALSE;
  1726.  
  1727.     while (*str) {
  1728.     if ((*str == '$') && (str[1] == '$')) {
  1729.         /*
  1730.          * A dollar sign may be escaped either with another dollar sign.
  1731.          * In such a case, we skip over the escape character and store the
  1732.          * dollar sign into the buffer directly.
  1733.          */
  1734.         str++;
  1735.         Buf_AddByte(buf, (Byte)*str);
  1736.         str++;
  1737.     } else if (*str != '$') {
  1738.         /*
  1739.          * Skip as many characters as possible -- either to the end of
  1740.          * the string or to the next dollar sign (variable invocation).
  1741.          */
  1742.         char  *cp;
  1743.  
  1744.         for (cp = str++; *str != '$' && *str != '\0'; str++) {
  1745.         ;
  1746.         }
  1747.         Buf_AddBytes(buf, str - cp, (Byte *)cp);
  1748.     } else {
  1749.         val = Var_Parse (str, ctxt, undefErr, &length, &doFree);
  1750.  
  1751.         /*
  1752.          * When we come down here, val should either point to the
  1753.          * value of this variable, suitably modified, or be NULL.
  1754.          * Length should be the total length of the potential
  1755.          * variable invocation (from $ to end character...)
  1756.          */
  1757.         if (val == var_Error || val == varNoError) {
  1758.         /*
  1759.          * If performing old-time variable substitution, skip over
  1760.          * the variable and continue with the substitution. Otherwise,
  1761.          * store the dollar sign and advance str so we continue with
  1762.          * the string...
  1763.          */
  1764.         if (oldVars) {
  1765.             str += length;
  1766.         } else if (undefErr) {
  1767.             /*
  1768.              * If variable is undefined, complain and skip the
  1769.              * variable. The complaint will stop us from doing anything
  1770.              * when the file is parsed.
  1771.              */
  1772.             if (!errorReported) {
  1773.             Parse_Error (PARSE_FATAL,
  1774.                      "Undefined variable \"%.*s\"",length,str);
  1775.             }
  1776.             str += length;
  1777.             errorReported = TRUE;
  1778.         } else {
  1779.             Buf_AddByte (buf, (Byte)*str);
  1780.             str += 1;
  1781.         }
  1782.         } else {
  1783.         /*
  1784.          * We've now got a variable structure to store in. But first,
  1785.          * advance the string pointer.
  1786.          */
  1787.         str += length;
  1788.         
  1789.         /*
  1790.          * Copy all the characters from the variable value straight
  1791.          * into the new string.
  1792.          */
  1793.         Buf_AddBytes (buf, strlen (val), (Byte *)val);
  1794.         if (doFree) {
  1795.             free ((Address)val);
  1796.         }
  1797.         }
  1798.     }
  1799.     }
  1800.     
  1801.     Buf_AddByte (buf, '\0');
  1802.     str = (char *)Buf_GetAll (buf, (int *)NULL);
  1803.     Buf_Destroy (buf, FALSE);
  1804.     return (str);
  1805. }
  1806.  
  1807. /*-
  1808.  *-----------------------------------------------------------------------
  1809.  * Var_GetTail --
  1810.  *    Return the tail from each of a list of words. Used to set the
  1811.  *    System V local variables.
  1812.  *
  1813.  * Results:
  1814.  *    The resulting string.
  1815.  *
  1816.  * Side Effects:
  1817.  *    None.
  1818.  *
  1819.  *-----------------------------------------------------------------------
  1820.  */
  1821. char *
  1822. Var_GetTail(file)
  1823.     char        *file;        /* Filename to modify */
  1824. {
  1825.     return(VarModify(file, VarTail, (ClientData)0));
  1826. }
  1827.  
  1828. /*-
  1829.  *-----------------------------------------------------------------------
  1830.  * Var_GetHead --
  1831.  *    Find the leading components of a (list of) filename(s).
  1832.  *    XXX: VarHead does not replace foo by ., as (sun) System V make
  1833.  *    does.
  1834.  *
  1835.  * Results:
  1836.  *    The leading components.
  1837.  *
  1838.  * Side Effects:
  1839.  *    None.
  1840.  *
  1841.  *-----------------------------------------------------------------------
  1842.  */
  1843. char *
  1844. Var_GetHead(file)
  1845.     char        *file;        /* Filename to manipulate */
  1846. {
  1847.     return(VarModify(file, VarHead, (ClientData)0));
  1848. }
  1849.  
  1850. /*-
  1851.  *-----------------------------------------------------------------------
  1852.  * Var_Init --
  1853.  *    Initialize the module
  1854.  *
  1855.  * Results:
  1856.  *    None
  1857.  *
  1858.  * Side Effects:
  1859.  *    The VAR_CMD and VAR_GLOBAL contexts are created 
  1860.  *-----------------------------------------------------------------------
  1861.  */
  1862. void
  1863. Var_Init ()
  1864. {
  1865.     VAR_GLOBAL = Targ_NewGN ("Global");
  1866.     VAR_CMD = Targ_NewGN ("Command");
  1867.  
  1868. }
  1869.  
  1870. /****************** PRINT DEBUGGING INFO *****************/
  1871. static
  1872. VarPrintVar (v)
  1873.     Var            *v;
  1874. {
  1875.     printf ("%-16s = %s\n", v->name, Buf_GetAll(v->val, (int *)NULL));
  1876.     return (0);
  1877. }
  1878.  
  1879. /*-
  1880.  *-----------------------------------------------------------------------
  1881.  * Var_Dump --
  1882.  *    print all variables in a context
  1883.  *-----------------------------------------------------------------------
  1884.  */
  1885. Var_Dump (ctxt)
  1886.     GNode          *ctxt;
  1887. {
  1888.     Lst_ForEach (ctxt->context, VarPrintVar);
  1889. }
  1890.